unit Chromium4D.GlobalHandler;

interface

  uses System.Classes, System.SysUtils, ceflib
    , Chromium4D.Classes
    , Chromium4D.Utils
    , Chromium4D.Render.Process
    ;
//  {$IFDEF WebKit_2454_UP}
type


  TSyV8GlobalHandler = class(TCefv8HandlerOwn)
  private
    const
      STR_V8_CODE =
          ' "use strict"; '                           + #10#13
        + ' Object.defineProperty(this, ''ncjs'', { ' + #10#13
        + '   get: function(){ '                      + #10#13
        + '     native function init(); '             + #10#13
        + '     delete this.ncjs; '                   + #10#13
        + '     return this.ncjs = init()'            + #10#13
        + '   },'                                     + #10#13
        + '   configurable: true, '                   + #10#13
        + '   enumerable: true '                      + #10#13
        + ' })';
  protected
    FProcess: TSyRenderProcess;
  protected
    function Execute(const name: ustring; const obj: ICefv8Value; const arguments: TCefv8ValueArray;
      var retval: ICefv8Value; var exception: ustring): Boolean; override;
  public
    constructor Create(AProcess: TSyRenderProcess); virtual;
    destructor Destroy; override;
  public
    class procedure RegisterExtension(AProcess: TSyRenderProcess);
  end;

//  {$ENDIF}

implementation

  uses Chromium4D.Render.Environment, Chromium4D.Extension.Process;

//{$IFDEF WebKit_2454_UP}

{ TSyV8GlobalHandler }

function Url2Path(url: string; var filePath: string): Boolean;
var
  parts: TUrlParts;
  path : string;
  pathlen: Integer;
  I: Integer;
begin
  if not CefParseUrl(url, parts) then
    Exit(false);

  if (parts.scheme <> 'file') then
    Exit(false);

  path    := parts.path;
  pathlen := Length(path);

  // windows : /C:/A/B.html
  if (pathlen > 3) and (path[3] = ':') then
  begin
    // convert '/' to '\\'
    for I := 0 to pathlen - 1 do
    begin
      if (path[I] = '/') then
        path[I] := '\';
    end;
  end;

  filePath := CefURIDecode(path, true, UU_SPACES);

  Result := True;
end;

constructor TSyV8GlobalHandler.Create(AProcess: TSyRenderProcess);
begin
  FProcess := AProcess;
  inherited Create;
end;

destructor TSyV8GlobalHandler.Destroy;
begin
  FProcess := nil;
  inherited;
end;

function TSyV8GlobalHandler.Execute(const name: ustring; const obj: ICefv8Value; const arguments: TCefv8ValueArray;
  var retval: ICefv8Value; var exception: ustring): Boolean;
var
  context: ICefv8Context;
  frame: ICefFrame;
  ncjsinit, process: ICefv8Value;
  ncjsinit_exception: ICefV8Exception;
  args: TCefv8ValueArray;
  frameurl, execPath, pagePath: string;
  env: TSyRenderEnvironment;
  ncjs: TStringList;
begin
  Logger.Trace('TSyV8GlobalHandler.Execute');

  context := TCefv8ContextRef.Current;
  frame   := context.Frame;
  frameurl:= frame.Url;
  execPath := FProcess.CommandLine.GetProgram;

  if not Url2Path(frameurl, pagePath) then
  begin
    exception := 'Invalid request source.';
    Exit(True)
  end;

  ncjs := TStringList.Create;
  try
    ncjs.LoadFromFile(ExtractFilePath(execPath) + '\native\nc.js');
    if not context.Eval(ncjs.Text, ncjsinit, ncjsinit_exception) then
    begin
      exception := 'Failed to evaluate nc.js';
      Exit(True)
    end;
  finally
    ncjs.Free;
  end;

  // Create node environment
  env     := TSyRenderEnvironment.Create(FProcess, context);
  process := TSyProcessHandler.Bind(FProcess, context.Browser, frame, context);
  env.Setup(execPath, pagePath, frameUrl, process);

  SetLength(args, 1);
  args[0] := process;
  retval  := ncjsinit.ExecuteFunction(obj, args);

  if not ((retval <> nil) and retval.IsObject) then
  begin
    exception := 'Failed to create nc.js object.';
    Exit(true);
  end;
end;

class procedure TSyV8GlobalHandler.RegisterExtension(AProcess: TSyRenderProcess);
begin
  if CefRegisterExtension('v8/Node-CEF-Delphi', STR_V8_CODE, TSyV8GlobalHandler.Create(AProcess)) then
    Logger.Trace('TSyV8GlobalHandler.RegisterExtension: v8/Node-CEF-Delphi');
end;

//{$ENDIF}

end.
